package tr.gov.tubitak.uekae.esya.api.xades.example.efatura;

import org.junit.Test;
import org.w3c.dom.Element;
import tr.gov.tubitak.uekae.esya.api.asn.x509.ECertificate;
import tr.gov.tubitak.uekae.esya.api.crypto.util.KeyUtil;
import tr.gov.tubitak.uekae.esya.api.smartcard.example.JSmartCardManager;
import tr.gov.tubitak.uekae.esya.api.xades.example.utils.SampleBase;
import tr.gov.tubitak.uekae.esya.api.xades.example.validation.Validation;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.*;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.model.Transform;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.model.Transforms;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.model.keyinfo.KeyValue;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.model.xades.ClaimedRole;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.model.xades.SignerRole;
import tr.gov.tubitak.uekae.esya.api.xmlsignature.util.XmlUtil;

import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileOutputStream;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.GregorianCalendar;

/**
 * @author: suleyman.uslu
 */
public class EFatura extends SampleBase {

    public static final String SIGNATURE_FILENAME = "imzalifatura.xml";

    private static XMLGregorianCalendar getTime(){
        Calendar cal = new GregorianCalendar();
        cal.get(Calendar.SECOND);
        return XmlUtil.createDate(cal);
    }

    /**
     * Signs efatura
     * @throws Exception
     */
    @Test
    public void signEFatura() throws Exception
    {
        // read XML file from document
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder db = dbf.newDocumentBuilder();
        // read from file, it can be stream too
        org.w3c.dom.Document faturaDoc = db.parse(new File(BASE_DIR + "imzasizfatura.xml"));

        // get the element that signature will be added
        Element extContent = (Element)faturaDoc.getDocumentElement().getElementsByTagName("ext:ExtensionContent").item(0);

        // generate signature
        Context context = createContext();
        context.setDocument(faturaDoc);
        XMLSignature signature = new XMLSignature(context, false);

        // attach signature to envelope structure
        extContent.appendChild(signature.getElement());

        // use enveloped signature transform
        Transforms transforms = new Transforms(context);
        transforms.addTransform(new Transform(context, TransformType.ENVELOPED.getUrl()));

        // add whole document(="") with envelope transform, with SHA256
        // and don't include it into signature(false)
        signature.addDocument("", "text/xml", transforms, DigestMethod.SHA_256, false);

        signature.getSignedInfo().setSignatureMethod(SignatureMethod.RSA_SHA256);

        // false-true gets non-qualified certificates while true-false gets qualified ones
        X509Certificate cert = JSmartCardManager.getInstance().getSignatureCertificate(true, false);

        // add certificate to show who signed the document
        signature.addKeyInfo(new ECertificate(cert.getEncoded()));

        // add signer role information
        SignerRole rol = new SignerRole(context, new ClaimedRole[]{new ClaimedRole(context, "Supplier")});
        signature.getQualifyingProperties().getSignedSignatureProperties().setSignerRole(rol);

        // e-fatura standards want public key info to be in the signature
        PublicKey pk = KeyUtil.decodePublicKey(new ECertificate(cert.getEncoded()).getSubjectPublicKeyInfo());
        signature.getKeyInfo().add(new KeyValue(context, pk));

        // add signing time
        signature.getQualifyingProperties().getSignedSignatureProperties().setSigningTime(getTime());

        signature.sign(JSmartCardManager.getInstance().getSigner(PIN, cert));

        // e-fatura standards want signatureID to be same with cbc:URI
        // get signatureID from e-fatura
        String signatureID = ((Element)(faturaDoc.getDocumentElement().getElementsByTagName("cbc:URI").item(0))).getTextContent();
        String signatureIDwoNumberSign = signatureID.substring(1);

        // change original signatureID
        Element dsSignature = (Element)(faturaDoc.getDocumentElement().getElementsByTagName("ds:Signature").item(0));
        dsSignature.setAttribute("Id", signatureIDwoNumberSign);

        Element xadesQualifyingProperties = (Element)(faturaDoc.getDocumentElement().getElementsByTagName("xades:QualifyingProperties").item(0));
        xadesQualifyingProperties.setAttribute("Target", signatureID);

        // write to file
        Source source = new DOMSource(faturaDoc);
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.transform(source, new StreamResult(new FileOutputStream(BASE_DIR + SIGNATURE_FILENAME)));
    }

    @Test
    public void validate() throws Exception {
        Validation.validate(SIGNATURE_FILENAME);
    }
}
